package com.kds.smarthome.aop;

import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.kds.smarthome.annotation.Log;
import com.kds.smarthome.entity.SysOperLog;
import com.kds.smarthome.service.SysOperLogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * 操作日志记录处理
 */
@Slf4j
@Aspect
@Component
public class LogAspect {

    @Autowired
    private SysOperLogService operLogService;

    @Autowired
    @Qualifier("ipLocationThreadPool")
    private ExecutorService ipLocationThreadPool;

    @Autowired
    @Qualifier("asyncTaskExecutor")
    private ExecutorService asyncTaskExecutor;

    // 需要脱敏的字段集合
    private static final Set<String> SENSITIVE_FIELDS = new HashSet<>();

    static {
        // 配置需要脱敏的字段名称
        SENSITIVE_FIELDS.add("password");
        SENSITIVE_FIELDS.add("oldPassword");
        SENSITIVE_FIELDS.add("newPassword");
        SENSITIVE_FIELDS.add("confirmPassword");
        SENSITIVE_FIELDS.add("idCard");
        SENSITIVE_FIELDS.add("cardNo");
        SENSITIVE_FIELDS.add("mobile");
        SENSITIVE_FIELDS.add("phone");
        SENSITIVE_FIELDS.add("email");
        SENSITIVE_FIELDS.add("token");
        SENSITIVE_FIELDS.add("secret");
        SENSITIVE_FIELDS.add("key");
    }

    // 配置切入点
    @Pointcut("@annotation(com.kds.smarthome.annotation.Log)")
    public void logPointCut() {
    }

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     * @param result    返回值
     */
    @AfterReturning(pointcut = "logPointCut()", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Object result) {
        handleLog(joinPoint, null, result);
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        handleLog(joinPoint, e, null);
    }

    protected void handleLog(final JoinPoint joinPoint, final Exception e, Object result) {
        try {
            // 获取注解
            Log controllerLog = getAnnotationLog(joinPoint);
            if (controllerLog == null) {
                return;
            }

            // 获取当前登录用户
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            String username = "匿名用户";
            if (authentication != null && authentication.isAuthenticated() && !"anonymousUser".equals(authentication.getPrincipal())) {
                username = authentication.getName();
            }

            // 获取请求信息
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            SysOperLog operLog = new SysOperLog();
            operLog.setStatus(0); // 默认为正常
            operLog.setOperName(username);

            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();
                String ip = getIpAddr(request);
                operLog.setOperIp(ip);
                operLog.setOperLocation(getIpLocation(ip));
                operLog.setOperUrl(request.getRequestURI());
                operLog.setRequestMethod(request.getMethod());

                // 解析User-Agent
                String userAgentStr = request.getHeader("User-Agent");
                if (userAgentStr != null) {
                    UserAgent userAgent = UserAgentUtil.parse(userAgentStr);
                    operLog.setDeptName(userAgent.getBrowser().getName() + " " + userAgent.getOs().getName());
                }
            }

            // 设置方法名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            operLog.setMethod(className + "." + methodName + "()");

            // 设置注解上的参数
            getControllerMethodDescription(joinPoint, controllerLog, operLog);

            // 设置操作时间
            operLog.setOperTime(new Date());

            // 是否需要保存请求的参数
            if (controllerLog.isSaveRequestData()) {
                // 获取参数，排除敏感信息如密码等
                String params = argsToString(joinPoint.getArgs());
                operLog.setOperParam(params);
            }

            // 是否需要保存响应的参数
            if (controllerLog.isSaveResponseData() && result != null) {
                String jsonResult = JSONUtil.toJsonStr(result);
                // 对响应结果中的敏感信息也进行脱敏
                jsonResult = filterSensitiveInfo(jsonResult);
                operLog.setJsonResult(jsonResult);
            }

            // 处理异常信息
            if (e != null) {
                operLog.setStatus(1); // 异常状态
                operLog.setErrorMsg(e.getMessage().substring(0, Math.min(e.getMessage().length(), 2000)));
            }

            // 真正异步保存日志
            asyncSaveLog(operLog);
        } catch (Exception ex) {
            log.error("记录操作日志失败", ex);
        }
    }

    /**
     * 获取注解信息
     */
    private Log getAnnotationLog(JoinPoint joinPoint) throws Exception {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        return method.getAnnotation(Log.class);
    }

    /**
     * 获取注解中对方法的描述信息
     */
    private void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog) {
        // 设置标题
        operLog.setTitle(log.title());
        // 设置业务类型
        operLog.setBusinessType(log.businessType());
        // 设置操作人类别
        operLog.setOperatorType(log.operatorType());
    }

    /**
     * 参数拼装
     */
    private String argsToString(Object[] paramsArray) {
        if (paramsArray == null || paramsArray.length == 0) {
            return "";
        }

        StringBuilder params = new StringBuilder();
        try {
            for (Object param : paramsArray) {
                if (param == null) {
                    continue;
                }

                // 转换为JSON字符串
                String jsonStr = JSONUtil.toJsonStr(param);

                // 过滤敏感信息
                jsonStr = filterSensitiveInfo(jsonStr);

                params.append(jsonStr).append(" ");
            }
        } catch (Exception e) {
            log.error("参数转换异常", e);
        }

        return params.toString().trim();
    }

    /**
     * 过滤JSON字符串中的敏感信息
     * @param jsonString JSON字符串
     * @return 过滤后的JSON字符串
     */
    private String filterSensitiveInfo(String jsonString) {
        if (StrUtil.isBlank(jsonString)) {
            return jsonString;
        }

        try {
            // 检查是否是JSON格式
            if (!jsonString.trim().startsWith("{") && !jsonString.trim().startsWith("[")) {
                return jsonString;  // 不是JSON格式，直接返回原字符串
            }

            // 解析JSON字符串
            JSONObject jsonObject = JSONUtil.parseObj(jsonString);

            // 处理敏感字段
            for (String field : SENSITIVE_FIELDS) {
                if (jsonObject.containsKey(field)) {
                    // 将敏感信息替换为星号
                    String value = jsonObject.getStr(field);
                    if (StrUtil.isNotBlank(value)) {
                        // 只保留前后各1位字符，中间用星号代替
                        int length = value.length();
                        if (length <= 2) {
                            jsonObject.set(field, "***");
                        } else {
                            jsonObject.set(field, value.charAt(0) + "****" + value.charAt(length - 1));
                        }
                    }
                }
            }

            // 转回JSON字符串
            return jsonObject.toString();
        } catch (Exception e) {
            // 如果解析失败，返回原字符串，但记录错误日志
            log.warn("脱敏处理失败: {}", e.getMessage());
            return jsonString;
        }
    }

    /**
     * 真正异步保存日志
     * 使用线程池执行，避免阻塞主线程
     */
    private void asyncSaveLog(final SysOperLog operLog) {
        asyncTaskExecutor.execute(() -> {
            try {
                operLogService.insertOperLog(operLog);
            } catch (Exception e) {
                log.error("异步保存操作日志异常", e);
            }
        });
    }

    /**
     * 获取客户端IP地址
     */
    private String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        // 对于通过多个代理的情况，第一个IP为客户端真实IP
        if (ip != null && ip.indexOf(",") > 0) {
            ip = ip.substring(0, ip.indexOf(","));
        }

        // 处理本地访问
        if ("127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip)) {
            ip = NetUtil.getLocalhostStr();
        }

        return ip;
    }

    /**
     * 获取IP地址所在地
     */
    private String getIpLocation(String ip) {
        if (ip == null || ip.isEmpty() || "127.0.0.1".equals(ip) || ip.startsWith("192.168.") || ip.startsWith("10.")) {
            return "内网IP";
        }

        // 异步获取IP地址位置信息
        Future<String> future = ipLocationThreadPool.submit(() -> {
            try {
                // 这里可以调用第三方服务或API获取IP地址位置信息
                // 简单示例，实际项目中可使用更可靠的服务
                return "未知位置";  // 实际项目中应替换为真实位置信息
            } catch (Exception e) {
                log.error("获取IP归属地异常: {}", e.getMessage());
                return "未知位置";
            }
        });

        try {
            // 等待结果，设置超时
            return future.get(2000, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            log.warn("获取IP归属地超时: {}", ip);
            future.cancel(true);
        }

        return "未知位置";
    }
}
